﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YasharEl.Infrastructure.Diagnostics.Logging;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Extensibility;
using System.Diagnostics.Contracts;
using System.Collections.Concurrent;
using YasharEl.Infrastructure.Threading;

namespace YasharEl.Infrastructure.ApplicationModel.Bootstrap
{
    internal class DelayedLogger<TExtension> : ILogger<BaseBootstrapper<TExtension>>
        where TExtension : IBootstrappingExtension
    {
        #region ReadOnly Fields

        private readonly ConcurrentQueue<DelayedLogEntry> _delayedLogEntriesQueue = new ConcurrentQueue<DelayedLogEntry>();

        #endregion

        #region ILogger Members

        public void Info(string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    LogMethod = "Info"
                }
            );
        }

        public void Info(Exception exception, string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    Exception = exception,
                    LogMethod = "Info"
                }
            );
        }

        public void Warn(string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    LogMethod = "Warn"
                }
            );
        }

        public void Warn(Exception exception, string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    Exception = exception,
                    LogMethod = "Warn"
                }
            );
        }

        public void Debug(string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    LogMethod = "Debug"
                }
            );
        }

        public void Debug(Exception exception, string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    Exception = exception,
                    LogMethod = "Debug"
                }
            );
        }

        public void Error(string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    LogMethod = "Error"
                }
            );
        }

        public void Error(Exception exception, string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    Exception = exception,
                    LogMethod = "Error"
                }
            );
        }

        public void Fatal(string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    LogMethod = "Fatal"
                }
            );
        }

        public void Fatal(Exception exception, string format, params object[] args)
        {
            _delayedLogEntriesQueue.Enqueue(
                new DelayedLogEntry()
                {
                    Format = format,
                    Args = args,
                    Exception = exception,
                    LogMethod = "Fatal"
                }
            );
        }

        #endregion

        #region Public Methods

        public void FlushDelayedLoggineEvents(ILogger<BaseBootstrapper<TExtension>> logger)
        {
            Contract.Requires<ArgumentNullException>(logger != null, "logger could not be null.");

            while (!_delayedLogEntriesQueue.IsEmpty)
            {
                DelayedLogEntry logEntry;
                if (_delayedLogEntriesQueue.TryDequeue(out logEntry))
                {
                    switch (logEntry.LogMethod)
                    {
                        case "Debug":
                            if (logEntry.Exception == null)
                                logger.Debug(logEntry.Format, logEntry.Args);
                            else
                                logger.Debug(logEntry.Exception, logEntry.Format, logEntry.Args);
                            break;
                        case "Info":
                            if (logEntry.Exception == null)
                                logger.Info(logEntry.Format, logEntry.Args);
                            else
                                logger.Info(logEntry.Exception, logEntry.Format, logEntry.Args);
                            break;
                        case "Warn":
                            if (logEntry.Exception == null)
                                logger.Warn(logEntry.Format, logEntry.Args);
                            else
                                logger.Warn(logEntry.Exception, logEntry.Format, logEntry.Args);
                            break;
                        case "Error":
                            if (logEntry.Exception == null)
                                logger.Error(logEntry.Format, logEntry.Args);
                            else
                                logger.Error(logEntry.Exception, logEntry.Format, logEntry.Args);
                            break;
                        case "Fatal":
                            if (logEntry.Exception == null)
                                logger.Fatal(logEntry.Format, logEntry.Args);
                            else
                                logger.Fatal(logEntry.Exception, logEntry.Format, logEntry.Args);
                            break;
                    }
                }
            }

            logger.Info("Flushing delayed logging events finished...");
        }

        #endregion

        private class DelayedLogEntry
        {
            public string Format { get; set; }
            public object[] Args { get; set; }
            public Exception Exception { get; set; }

            public string LogMethod { get; set; }
        }

        public bool IsDebugEnabled
        {
            get { return true; }
        }

        public bool IsErrorEnabled
        {
            get { return true; }
        }

        public bool IsFatalEnabled
        {
            get { return true; }
        }

        public bool IsInfoEnabled
        {
            get { return true; }
        }

        public bool IsWarnEnabled
        {
            get { return true; }
        }
    }
}
